package com.threegroup.web.service;


import com.alibaba.fastjson2.JSON;
import com.threegroup.common.exception.TakeOutException;
import com.threegroup.common.utils.AssertUtil;
import com.threegroup.common.utils.Result;
import com.threegroup.common.utils.UserUtils;
import com.threegroup.web.dao.AddressBookEntityMapper;
import com.threegroup.web.pojo.AddressBookES;
import com.threegroup.web.pojo.AddressBookEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class AddressBookService {
    @Resource
    private AddressBookEntityMapper addressBookEntityMapper;

    @Resource
    private UserUtils userUtils;

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    public static Integer TIME_OUT_CACHE = 60*60*24;
    /**
     * 添加地址
     * @param addressBookDTO
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public Result add(AddressBookEntity addressBookDTO) {
        setInfo(addressBookDTO);
        //非空判断
        notNull(addressBookDTO);
        //添加成功则count=1
        int count=addressBookEntityMapper.insert(addressBookDTO);
        AssertUtil.isTrue(count<0,"添加失败！");

        Result result=new Result();
        result.setMsg("添加成功！");
        result.setCode(0);
        result.setData(addressBookDTO);
        return result;
    }

    /**
     * 获取未填入对应值，并设置
     * id不用设置
     */
    private void setInfo(AddressBookEntity addressBookDTO) {
        Long userId = userUtils.getUserId();

        //获取数据，设置对应值
        addressBookDTO.setIsDefault(0);
        addressBookDTO.setCreator(userId);
        addressBookDTO.setCreateDate(new Date());
        addressBookDTO.setUpdater(userId);
        addressBookDTO.setUpdateDate(new Date());
        addressBookDTO.setUserId(userId);
        addressBookDTO.setIsDeleted(0);
    }

    /**
     * 非空判断
     * @param addressBookDTO
     */
    private void notNull(AddressBookEntity addressBookDTO) {
        Long userId = userUtils.getUserId();
        //输入内容非空判断
        AssertUtil.isTrue(null==addressBookDTO,"请输入有效信息！");
        AssertUtil.isTrue(null==addressBookDTO.getConsignee(),"请输入姓名！");
        AssertUtil.isTrue(null==addressBookDTO.getGender(),"请选择性别！");
        AssertUtil.isTrue(null==addressBookDTO.getPhone(),"请输入电话！");
        AssertUtil.isTrue(null==addressBookDTO.getDetail(),"请输入地址！");

        //判断未输入内容非空
        AssertUtil.isTrue(null==userId||null==addressBookDTO.getUserId()
                ||null==addressBookDTO.getUpdater()||null==addressBookDTO.getCreator(),"请输入有效userId！");
        AssertUtil.isTrue(null==addressBookDTO.getCreateDate(),"请输入创建时间！");
        AssertUtil.isTrue(null==addressBookDTO.getUpdateDate(),"请输入修改时间！");
        AssertUtil.isTrue(null==addressBookDTO.getIsDeleted(),"请输入是否删除！");
    }

    /**
     * 地址列表
     * @return
     */
    public List<AddressBookEntity> queryAddr() {
        Long userId=userUtils.getUserId();
        return addressBookEntityMapper.queryAddr(userId);
    }

    /**
     * 修改地址
     * @param addressBookDTO
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public Result update(AddressBookEntity addressBookDTO) {

        addressBookDTO.setCreateDate(new Date());
        addressBookDTO.setUpdateDate(new Date());

        //添加成功则count=1
        int count=addressBookEntityMapper.updateByPrimaryKey(addressBookDTO);
        AssertUtil.isTrue(count<0,"修改失败！");

        Result result=new Result();
        result.setMsg("修改成功！");
        result.setCode(0);
        result.setData(addressBookDTO);
        return result;
    }

    /**
     * 删除地址
     * @param id
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public Result delete(Long id) {
        AssertUtil.isTrue(null==id,"请输入有效id！");
        //删除成功则count=1
        int count=addressBookEntityMapper.deleteByPrimaryKey(id);
        AssertUtil.isTrue(count<0,"删除失败！");

        Result result=new Result();
        result.setMsg("删除成功！");
        result.setCode(0);
        return result;
    }

    /**
     * 显示默认地址
     * @param userId
     * @return
     */
    public Result defaultAdrr(Long userId) {

        AddressBookEntity addressBook=addressBookEntityMapper.defaultAdrr(userId);
        Result result=new Result();
        result.setCode(0);
        result.setData(addressBook);
        return result;
    }

    /**
     * 设置默认地址
     * @param addressBookDTO
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public Result setDefaultAdrr(AddressBookEntity addressBookDTO) {
        Long userId=userUtils.getUserId();
        int count1=addressBookEntityMapper.setDefaultAdrrFirst(userId);
        AssertUtil.isTrue(count1<0,"默认地址1设置失败！");
        int count2=addressBookEntityMapper.setDefaultAdrrSecond(addressBookDTO);
        AssertUtil.isTrue(count2<0,"默认地址2设置失败！");
        Result result=new Result();
        result.setCode(0);
        result.setMsg("默认地址设置成功！");
        return result;
    }

    /**
     * id返回地址详细信息
     * @return
     * @param id
     */
    public Result selectByPrimaryKey(Long id) {

        /**
         * 结果集
         */
        Result result=new Result();

        /**
         *准备redis模板
         */
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        //非空校验
        AssertUtil.isTrue(null==id,"请输入有效id！");

        String idRedis=String.valueOf(id);

        /**
         * 首先查询redis是否存在信息
         */
        AddressBookES addressBookEsFromRedis = null;
        String s = opsForValue.get(idRedis);
        //存在就执行返回
        if (StringUtils.isNoneBlank(s)){
            //如果查到的是数据库里没有的空，则直接返回
            if ("{}".equals(s)){
                throw new TakeOutException(302,"待查询的记录不存在");
            }
            //否则正常转型并接收，返回查询的结果
            //转型并接收
            addressBookEsFromRedis=  JSON.parseObject(s,AddressBookES.class);
            //添加结果
            result.setData(addressBookEsFromRedis);
            //并且把该数据有效期重置1天
            stringRedisTemplate.expire(idRedis,TIME_OUT_CACHE, TimeUnit.SECONDS);
            //返回
            return result;
        }

        /**
         * 如果该热门数据不存在则直接访问数据库，则出现缓存击穿
         * 1、如果是高并发情况下，则需要把查询数据库的操作，进行加锁，
         * 2、应该是针对不同的商品id上锁，而不是简单上锁，
         * 现在多是分布式，要添加分布式锁，用setnx(setIfAbsent)实现
         */
        String sycn = idRedis+"sycn";
        Boolean test = opsForValue.setIfAbsent(sycn, "test");//true-执行成功  false-执行添加失败
        if (test!=true){
            throw new TakeOutException(303,"系统繁忙请稍后重试");
        }

        /**
         * 再次查询redis是否存在信息
         */
        s = opsForValue.get(idRedis);
        //存在就执行返回
        //存在就执行返回
        if (StringUtils.isNoneBlank(s)){
            //如果查到的是数据库里没有的空，则直接返回
            if ("{}".equals(s)){
                throw new TakeOutException(302,"待查询的记录不存在");
            }
            //否则正常转型并接收，返回查询的结果
            //转型并接收
            addressBookEsFromRedis=  JSON.parseObject(s,AddressBookES.class);
            //添加结果
            result.setData(addressBookEsFromRedis);
            //并且把该数据有效期重置1天
            stringRedisTemplate.expire(idRedis,TIME_OUT_CACHE, TimeUnit.SECONDS);
            //返回
            return result;
        }

        //查询数据库
        AddressBookEntity addressBook=addressBookEntityMapper.selectByPrimaryKey(id);

        /**
         * 把查到的结果备份到redis
         */
        String JsondishES = JSON.toJSONString(addressBook);
        opsForValue.set(idRedis,JsondishES,TIME_OUT_CACHE,TimeUnit.SECONDS);//设置有效时间1天


        /**
         *
         * 删除分布式锁
         */
        stringRedisTemplate.delete(sycn);

        result.setCode(0);
        result.setData(addressBook);
        return result;
    }
}